package com.chennan.leading.common.distributedno;

import com.chennan.leading.common.cache.impl.RedisUtil;
import com.chennan.leading.common.distributedno.model.BasicGenerateNoParam;
import com.chennan.leading.common.util.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.locks.Lock;

/**
 * @author chennan
 * @createTime 2022年03月23日 16:55:00
 */
@Slf4j
public abstract class AbstractGenerateNoBasic {

    @Autowired
    private RedisUtil redisUtil;

//    @Autowired
    private RedisLockRegistry lockRegistry;

    public String generateNo(){
        String today = TimeUtils.format(LocalDate.now(), TimeUtils.DATE_FORMAT_TYPE5);
        BasicGenerateNoParam generateNoParams = generateDistributedLockKey();
        // 必要参数检查
        checkParams(generateNoParams);
        // 编号序列按天分布式锁
        String noKey = String.format(generateNoParams.getRedisKeyNo(), today);
        generateNoDoWithLock(noKey, generateNoParams);
        Long increment = incrementNoDoWithRedis(noKey, generateNoParams);
        // 根据格式化类型生成最终编号序列
        String resultNo = formatNoDoWithType(today, increment, generateNoParams);
        return resultNo;
    }
    /**
     * 根据格式化类型生成最终编号
     *
     * @param today
     * @param increment
     * @param generateNoParams
     * @return
     */
    private String formatNoDoWithType(String today, Long increment, BasicGenerateNoParam generateNoParams) {
        if (!generateNoParams.getNumberNoType()) {
            return generateNoParams.getPrefix() + today
                    + String.format(generateNoParams.getFormatType(), increment);
        } else {
            return String.format(generateNoParams.getFormatType(), increment);
        }
    }
    /**
     * 通过redis自增原子性获取next编号序列
     *
     * @param noKey
     * @param generateNoParams
     * @return
     */
    private Long incrementNoDoWithRedis(String noKey, BasicGenerateNoParam generateNoParams) {
        Long increment = 1L;
        if (!StringUtils.isBlank(redisUtil.get(noKey))) {
            try {
                increment = redisUtil.incr(noKey);
            } catch (Exception e) {
                log.error("AbstractGenerateNoBasic generateNo  redisTemplate.increment error:", e);
                throw new RuntimeException("AbstractGenerateNoBasic generateNo  redisTemplate increment error");
            }
            if (Objects.nonNull(increment)) {
                increment %= generateNoParams.getMaxNo();
            }
        }
        return increment;
    }
    /**
     * 通过分布式锁检查生成编号序列
     *
     * @param noKey
     * @param generateNoParams
     */
    private void generateNoDoWithLock(String noKey, BasicGenerateNoParam generateNoParams) {
        if (StringUtils.isBlank(redisUtil.get(noKey))) {
            Lock lock = lockRegistry.obtain(generateNoParams.getDistributedLockKey());
            lock.lock();
            try {
                LocalDateTime startTime = LocalDate.now().atStartOfDay();
                LocalDateTime endTime = LocalDate.now().plusDays(1).atStartOfDay();
                if (StringUtils.isBlank(redisUtil.get(noKey))) {
                    Long count = findNoLasted(startTime, endTime);
                    redisUtil.set(noKey, String.valueOf(count), generateNoParams.getTimeout());
                }
            } catch (Throwable e) {
                log.error("AbstractGenerateNoBasic  generateNo simpleDistributedLockService.doWithLoc error:", e);
            }finally {
                lock.unlock();
            }

        }
    }

    protected abstract Long findNoLasted(LocalDateTime startTime, LocalDateTime endTime);

    protected abstract BasicGenerateNoParam generateDistributedLockKey();

    /**
     * 必要校验检查
     */
    private void checkParams(BasicGenerateNoParam generateNoParams) {
        Objects.requireNonNull(generateNoParams,
                "AbstractGenerateNoBasic subclass must be generateDistributedLockKey method,can not return null object");
        checkNull(generateNoParams.getDistributedLockKey(), "DistributedLockKey");
        checkNull(generateNoParams.getRedisKeyNo(), "RedisKeyNo");
        checkString(generateNoParams.getDistributedLockKey(), "DistributedLockKey");
        checkString(generateNoParams.getRedisKeyNo(), "RedisKeyNo");
    }
    /**
     * 检查是否是空字符串
     *
     * @param params
     *            检查的参数
     * @param columnName
     *            检查的属性名称
     */
    private void checkString(String params, String columnName) {
        if (StringUtils.isBlank(params)) {
            throw new RuntimeException(
                    "AbstractGenerateNoBasic subclass must be generateDistributedLockKey method column " + columnName
                            + " key can not ''");
        }
    }

    /**
     * 检查是否为空
     *
     * @param param
     *            检查的参数
     * @param column
     *            检查的属性名称
     */
    private void checkNull(String param, String column) {
        Objects.requireNonNull(param,
                "AbstractGenerateNoBasic subclass must be generateDistributedLockKey method " + column
                        + " key can not null");
    }



    public static void main(String[] args) {
        System.out.println(1%20);
    }
}
